package com.gitee.service.openapi;

import com.gitee.gateway.core.utils.StringUtils;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.http.Cookie;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.User;
import io.vertx.ext.auth.authentication.AuthenticationProvider;
import io.vertx.ext.auth.authentication.Credentials;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.HttpException;
import io.vertx.ext.web.handler.impl.HTTPAuthorizationHandler;

/**
 * 实现 Bearer Token + Cookie 认证双组合
 */
class TokenAuthHandler extends HTTPAuthorizationHandler<AuthenticationProvider> {

    public TokenAuthHandler(AuthenticationProvider authProvider, String realm) {
        super(authProvider, Type.BEARER, realm);
    }

    public static TokenAuthHandler create(TokenHandler tokenHandler) {
        return new TokenAuthHandler((credentials, resultHandler) -> {
            String token = credentials.getString("token");
            //验证 token 是否有效
            if (StringUtils.isValidToken(token)) {
                //读取账号信息
                JsonObject account = tokenHandler.user(token);
                if (account != null) {
                    resultHandler.handle(Future.succeededFuture(User.create(account)));
                    return;
                }
            }
            resultHandler.handle(Future.failedFuture(new HttpException(HttpResponseStatus.UNAUTHORIZED.code())));
        }, null);
    }

    @Override
    public void authenticate(RoutingContext context, Handler<AsyncResult<User>> handler) {
        parseAuthorization(context, parseAuthorization -> {
            String token = null;
            if (parseAuthorization.failed()) {
                //如果 Bearar 认证失败，则尝试使用 Cookie 认证(主要用于读取营业执照图片等操作)
                Cookie cookie = context.request().getCookie("itoken");
                token = cookie != null ? cookie.getValue() : null;
                if (token == null) {
                    handler.handle(Future.failedFuture(parseAuthorization.cause()));
                    return;
                }
            } else
                token = parseAuthorization.result();

            authProvider.authenticate(new BearerTokenCredentials(token), authn -> {
                if (authn.failed()) {
                    handler.handle(Future.failedFuture(new HttpException(HttpResponseStatus.UNAUTHORIZED.code(), authn.cause())));
                } else {
                    handler.handle(authn);
                }
            });
        });
    }

    record BearerTokenCredentials(String token) implements Credentials {
        @Override
        public JsonObject toJson() {
            return new JsonObject().put("token", token);
        }
    }
}
